using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Windows.Forms;

namespace ICP_MS.Plugin.Tune;

public class NewAutoTuneExperiment
{
	public class TuneIsotope
	{
		public int Id { get; set; }

		public string Symbol { get; set; }

		public double Mass { get; set; }

		public int Dwell { get; set; }

		public double Result { get; set; }
	}

	public class TuneRatio
	{
		public int Id { get; set; }

		public TuneIsotope Numerator { get; set; }

		public TuneIsotope Denominator { get; set; }

		public int Dwell { get; set; }
	}

	public class DeviceCondition
	{
		public int Id { get; set; }

		public bool Selected { get; set; }

		public bool Force { get; set; }

		public string Device { get; set; }

		public double MinValue { get; set; }

		public double MaxValue { get; set; }

		public double DefaultValue { get; set; }

		public double Step { get; set; }

		public double ReadErr { get; set; }

		public double FinishErr { get; set; }

		public double BestValue { get; set; }

		public bool Used { get; set; }

		public DeviceCondition Clone()
		{
			DeviceCondition device = new DeviceCondition();
			device.Id = Id;
			device.Device = Device;
			device.Selected = Selected;
			device.Force = Force;
			device.MinValue = MinValue;
			device.MaxValue = MaxValue;
			device.DefaultValue = DefaultValue;
			device.Step = Step;
			device.ReadErr = ReadErr;
			device.FinishErr = FinishErr;
			return device;
		}

		public void Copy(DeviceCondition dev)
		{
			if (Id == dev.Id)
			{
				Force = dev.Force;
				MinValue = dev.MinValue;
				MaxValue = dev.MaxValue;
				DefaultValue = dev.DefaultValue;
				Step = dev.Step;
				ReadErr = dev.ReadErr;
				FinishErr = dev.FinishErr;
			}
		}
	}

	public class StageResult1
	{
		public double Y { get; set; }

		public double X { get; set; }

		public double result { get; set; }
	}

	public class StageResult2
	{
		public double CarrierGas { get; set; }

		public double Power { get; set; }

		public double Result { get; set; }
	}

	public class StageResult3
	{
		public double Ext { get; set; }

		public double L1 { get; set; }

		public double L2 { get; set; }

		public double L3 { get; set; }

		public double result { get; set; }
	}

	public class StageResult4
	{
		public double Focus { get; set; }

		public double D1 { get; set; }

		public double D2 { get; set; }

		public double DA { get; set; }

		public double QE { get; set; }

		public double Result { get; set; }
	}

	public class ParamConditon
	{
		public int Id { get; set; }

		public string Symbol { get; set; }

		public TuneIsotope Isotope { get; set; }

		public TuneRatio Ratio { get; set; }

		public int Type { get; set; }

		public double Limit { get; set; }

		public double Result { get; set; }

		public bool Pass { get; set; }

		public double BestResult { get; set; }

		public ParamConditon Clone()
		{
			ParamConditon param = new ParamConditon();
			param.Id = Id;
			param.Symbol = Symbol;
			param.Isotope = Isotope;
			param.Ratio = Ratio;
			param.Type = Type;
			param.Limit = Limit;
			return param;
		}
	}

	public class TuneStage
	{
		public int Index { get; set; }

		public bool Used { get; set; }

		public string StageName { get; set; }

		public int Iterations { get; set; }

		public int Sweeps { get; set; }

		public List<DeviceCondition> Devices { get; set; }

		public List<ParamConditon> Params { get; set; }

		public bool Selected { get; set; }

		public void UpdateDevice(DeviceCondition dev)
		{
			foreach (DeviceCondition stageDev in Devices)
			{
				if (stageDev.Id == dev.Id)
				{
					stageDev.Copy(dev);
				}
			}
		}
	}

	private int currentIndex = 0;

	public double StageIndex = -1.0;

	private List<double> massX = new List<double>();

	private Thread threadTune = null;

	private AutoResetEvent scanEvent = new AutoResetEvent(initialState: false);

	public List<TuneIsotope> Isotopes { get; set; }

	public List<TuneRatio> Ratios { get; set; }

	public List<DeviceCondition> Devices { get; set; }

	public List<ParamConditon> Params { get; set; }

	public List<TuneStage> Stages { get; set; }

	public TuneStage CurrentStage
	{
		get
		{
			if (currentIndex < 0)
			{
				return null;
			}
			if (currentIndex == Stages.Count)
			{
				TuneStage stage = new TuneStage();
				stage.Used = false;
				stage.Params = new List<ParamConditon>();
				stage.Devices = new List<DeviceCondition>();
				stage.Sweeps = 10;
				stage.Iterations = 1;
				stage.StageName = $"Stage - {currentIndex + 1}";
				stage.Selected = true;
				UpdateTuneStage(stage);
				Stages.Add(stage);
			}
			return Stages[currentIndex];
		}
	}

	public TuneStage FirstStage
	{
		get
		{
			currentIndex = 0;
			return CurrentStage;
		}
	}

	public TuneStage LastStage
	{
		get
		{
			currentIndex = Stages.Count - 1;
			return CurrentStage;
		}
	}

	public bool IsFirstStage => currentIndex == 0;

	public string Name { get; set; }

	public string Description { get; set; }

	public string Solution { get; set; }

	public bool isTuning { get; set; }

	public bool TuneSucceed { get; set; }

	public event EventHandler EventTuneFinish;

	public event EventHandler<MsgEventArgs> EventTuneMsg;

	public event EventHandler<PointEventMsg> EventPointMsg;

	public void SelectLastStage()
	{
		currentIndex--;
	}

	public void SelectNextStage()
	{
		currentIndex++;
	}

	public bool CheckFrontStage()
	{
		for (int i = currentIndex; i >= 0; i--)
		{
			if (Stages[i].Used)
			{
				return true;
			}
		}
		return false;
	}

	public bool CheckRearStage()
	{
		for (int i = currentIndex; i < Stages.Count; i++)
		{
			if (Stages[i].Used)
			{
				return true;
			}
		}
		return false;
	}

	public bool RemoveCurrentStage(bool direction)
	{
		Stages.RemoveAt(currentIndex);
		currentIndex = (direction ? (currentIndex + 1) : (currentIndex - 1));
		return false;
	}

	public NewAutoTuneExperiment()
	{
		Isotopes = new List<TuneIsotope>();
		Ratios = new List<TuneRatio>();
		Params = new List<ParamConditon>();
		Devices = new List<DeviceCondition>();
		Stages = new List<TuneStage>();
	}

	public void InitTuneDevices()
	{
		if (Devices == null || Devices.Count == 0)
		{
			Devices.Add(new DeviceCondition
			{
				Id = 0,
				Device = "水平位置",
				Force = true,
				MaxValue = 16.0,
				MinValue = 2.0,
				DefaultValue = 5.0,
				Step = 0.1,
				ReadErr = 0.05,
				FinishErr = 0.05
			});
			Devices.Add(new DeviceCondition
			{
				Id = 1,
				Device = "垂直位置",
				Force = true,
				MaxValue = 16.0,
				MinValue = 2.0,
				DefaultValue = 5.0,
				Step = 0.1,
				ReadErr = 0.05,
				FinishErr = 0.05
			});
			Devices.Add(new DeviceCondition
			{
				Id = 13,
				Device = "载气",
				Force = true,
				MaxValue = 1.5,
				MinValue = 0.9,
				DefaultValue = 1.1,
				Step = 0.01,
				ReadErr = 0.1,
				FinishErr = 0.01
			});
			Devices.Add(new DeviceCondition
			{
				Id = 17,
				Device = "功率",
				Force = true,
				MaxValue = 1800.0,
				MinValue = 1200.0,
				DefaultValue = 1400.0,
				Step = 10.0,
				ReadErr = 50.0,
				FinishErr = 5.0
			});
			Devices.Add(new DeviceCondition
			{
				Id = 2,
				Device = "采样深度",
				Force = true,
				MaxValue = 16.0,
				MinValue = 2.0,
				DefaultValue = 5.0,
				Step = 0.1,
				ReadErr = 0.05,
				FinishErr = 0.05
			});
			Devices.Add(new DeviceCondition
			{
				Id = 3,
				Device = "提取透镜",
				Force = true,
				MaxValue = -300.0,
				MinValue = -750.0,
				DefaultValue = -500.0,
				Step = 10.0,
				ReadErr = 0.5,
				FinishErr = 10.0
			});
			Devices.Add(new DeviceCondition
			{
				Id = 4,
				Device = "透镜(L1)",
				Force = true,
				MaxValue = -800.0,
				MinValue = -1600.0,
				DefaultValue = -1200.0,
				Step = 10.0,
				ReadErr = 0.5,
				FinishErr = 5.0
			});
			Devices.Add(new DeviceCondition
			{
				Id = 5,
				Device = "透镜(L2)",
				Force = true,
				MaxValue = -60.0,
				MinValue = -100.0,
				DefaultValue = -80.0,
				Step = 1.0,
				ReadErr = 0.5,
				FinishErr = 1.0
			});
			Devices.Add(new DeviceCondition
			{
				Id = 11,
				Device = "透镜(L3)",
				Force = true,
				MaxValue = -150.0,
				MinValue = -200.0,
				DefaultValue = -180.0,
				Step = 5.0,
				ReadErr = 0.5,
				FinishErr = 5.0
			});
			Devices.Add(new DeviceCondition
			{
				Id = 6,
				Device = "聚焦(Focus)",
				Force = true,
				MaxValue = 8.0,
				MinValue = 18.0,
				DefaultValue = 13.5,
				Step = 0.5,
				ReadErr = 0.5,
				FinishErr = 0.5
			});
			Devices.Add(new DeviceCondition
			{
				Id = 7,
				Device = "偏转(D1)",
				Force = true,
				MaxValue = -10.0,
				MinValue = -200.0,
				DefaultValue = -150.0,
				Step = 1.0,
				ReadErr = 0.5,
				FinishErr = 1.0
			});
			Devices.Add(new DeviceCondition
			{
				Id = 8,
				Device = "偏转(D2)",
				Force = true,
				MaxValue = -10.0,
				MinValue = -60.0,
				DefaultValue = -25.0,
				Step = 1.0,
				ReadErr = 0.5,
				FinishErr = 1.0
			});
			Devices.Add(new DeviceCondition
			{
				Id = 9,
				Device = "四极前置(QE)",
				Force = true,
				MaxValue = -10.0,
				MinValue = -60.0,
				DefaultValue = -35.0,
				Step = 1.0,
				ReadErr = 0.5,
				FinishErr = 0.5
			});
			Devices.Add(new DeviceCondition
			{
				Id = 10,
				Device = "六极杆偏置",
				Force = true,
				MaxValue = -30.0,
				MinValue = -20.0,
				DefaultValue = -25.0,
				Step = 0.5,
				ReadErr = 0.5,
				FinishErr = 1.0
			});
			Devices.Add(new DeviceCondition
			{
				Id = 12,
				Device = "DA",
				Force = true,
				MaxValue = -20.0,
				MinValue = -100.0,
				DefaultValue = -60.0,
				Step = 5.0,
				ReadErr = 0.5,
				FinishErr = 5.0
			});
			Devices.Add(new DeviceCondition
			{
				Id = 14,
				Device = "冷却气",
				Force = true,
				MaxValue = 16.0,
				MinValue = 10.0,
				DefaultValue = 13.0,
				Step = 0.5,
				ReadErr = 0.1,
				FinishErr = 0.1
			});
			Devices.Add(new DeviceCondition
			{
				Id = 15,
				Device = "辅助气",
				Force = true,
				MaxValue = 1.6,
				MinValue = 0.5,
				DefaultValue = 0.8,
				Step = 0.1,
				ReadErr = 0.1,
				FinishErr = 0.005
			});
			Devices.Add(new DeviceCondition
			{
				Id = 16,
				Device = "碰撞气",
				Force = false,
				MaxValue = 3.0,
				MinValue = 0.0,
				DefaultValue = 1.5,
				Step = 0.1,
				ReadErr = 0.1,
				FinishErr = 0.05
			});
		}
	}

	public bool CheckPosition(double value, DeviceCondition dev)
	{
		switch (dev.Id)
		{
		case 0:
			if (Math.Abs(PlatManager.plat.ReadX / 100.0 - value) <= value * dev.ReadErr)
			{
				return true;
			}
			break;
		case 1:
			if (Math.Abs(PlatManager.plat.ReadZ / 100.0 - value) <= value * dev.ReadErr)
			{
				return true;
			}
			break;
		case 2:
			if (Math.Abs(PlatManager.plat.ReadY / 100.0 - value) <= value * dev.ReadErr)
			{
				return true;
			}
			break;
		case 3:
			if (Math.Abs(LensManager.x2lens.ReadEx - value) <= Math.Abs(value) * 0.5)
			{
				return true;
			}
			break;
		case 4:
			if (Math.Abs(LensManager.x2lens.ReadL1 - value) <= Math.Abs(value) * dev.ReadErr)
			{
				return true;
			}
			break;
		case 5:
			if (Math.Abs(LensManager.x2lens.ReadL2 - value) <= Math.Abs(value) * dev.ReadErr)
			{
				return true;
			}
			break;
		case 6:
			if (Math.Abs(LensManager.x2lens.ReadF - value) <= Math.Abs(value) * dev.ReadErr)
			{
				return true;
			}
			break;
		case 7:
			if (Math.Abs(LensManager.x2lens.ReadD1 - value) <= Math.Abs(value) * dev.ReadErr)
			{
				return true;
			}
			break;
		case 8:
			if (Math.Abs(LensManager.x2lens.ReadD2 - value) <= Math.Abs(value) * dev.ReadErr)
			{
				return true;
			}
			break;
		case 9:
			if (Math.Abs(LensManager.x2lens.ReadQE - value) <= Math.Abs(value) * dev.ReadErr)
			{
				return true;
			}
			break;
		case 10:
			if (Math.Abs(LensManager.x2lens.ReadPole - value) <= Math.Abs(value) * dev.ReadErr)
			{
				return true;
			}
			break;
		case 11:
			if (Math.Abs(LensManager.x2lens.ReadL3 - value) <= Math.Abs(value) * dev.ReadErr)
			{
				return true;
			}
			break;
		case 12:
			if (Math.Abs(LensManager.x2lens.ReadDA - value) <= Math.Abs(value) * dev.ReadErr)
			{
				return true;
			}
			break;
		case 13:
			if (Math.Abs(PlasmaManager.plasma.ReadNebuliser - value) <= value * dev.ReadErr)
			{
				return true;
			}
			break;
		case 14:
			if (Math.Abs(PlasmaManager.plasma.ReadCool - value) <= value * dev.ReadErr)
			{
				return true;
			}
			break;
		case 15:
			if (Math.Abs(PlasmaManager.plasma.ReadAuxiliary - value) <= value * dev.ReadErr)
			{
				return true;
			}
			break;
		case 16:
			if (Math.Abs(PlasmaManager.plasma.ReadCollision - value) <= value * dev.ReadErr)
			{
				return true;
			}
			break;
		}
		return false;
	}

	public void UpdateTuneDevices()
	{
		List<DeviceCondition> selectDevices = Devices.Where((DeviceCondition device) => device.Selected).ToList();
		foreach (TuneStage stage2 in Stages)
		{
			stage2.Devices.RemoveAll((DeviceCondition stageDev) => !selectDevices.Exists((DeviceCondition dev) => dev.Id == stageDev.Id));
		}
		foreach (TuneStage stage in Stages)
		{
			foreach (DeviceCondition dev2 in selectDevices)
			{
				if (!stage.Devices.Exists((DeviceCondition stageDev) => stageDev.Id == dev2.Id))
				{
					stage.Devices.Add(dev2.Clone());
				}
				else
				{
					stage.UpdateDevice(dev2);
				}
			}
		}
	}

	public void UpdateTuneParams()
	{
		Params.Clear();
		foreach (TuneIsotope isotope in Isotopes)
		{
			Params.Add(new ParamConditon
			{
				Id = 0,
				Isotope = isotope,
				Ratio = null,
				Symbol = isotope.Symbol,
				Type = 0,
				Limit = 0.0
			});
		}
		foreach (TuneRatio ratio in Ratios)
		{
			Params.Add(new ParamConditon
			{
				Id = 0,
				Isotope = null,
				Ratio = ratio,
				Symbol = ratio.Numerator.Symbol + "/" + ratio.Denominator.Symbol,
				Type = 0,
				Limit = 0.0
			});
		}
		foreach (TuneStage stage2 in Stages)
		{
			stage2.Params.RemoveAll((ParamConditon stagePara) => !Params.Exists((ParamConditon para) => para.Symbol == stagePara.Symbol));
		}
		foreach (TuneStage stage in Stages)
		{
			foreach (ParamConditon para2 in Params)
			{
				if (!stage.Params.Exists((ParamConditon stagePara) => stagePara.Symbol == para2.Symbol))
				{
					stage.Params.Add(para2.Clone());
				}
			}
		}
	}

	public void UpdateTuneStage(TuneStage stage)
	{
		foreach (DeviceCondition device in Devices.Where((DeviceCondition dev) => dev.Selected))
		{
			stage.Devices.Add(device.Clone());
		}
		foreach (ParamConditon para in Params)
		{
			stage.Params.Add(para.Clone());
		}
	}

	public bool SaveConfig(string name)
	{
		string path = Application.StartupPath + "\\NewAutotune\\" + name + ".xml";
		XMLFile.Save(path, this);
		return true;
	}

	public bool DeleteConfig(string name)
	{
		string path = Application.StartupPath + "\\NewAutotune\\" + name + ".xml";
		if (File.Exists(path))
		{
			File.Delete(path);
		}
		return true;
	}

	public static string[] LoadAllConfigs()
	{
		string dir = Application.StartupPath + "\\NewAutotune";
		if (!Directory.Exists(dir))
		{
			Directory.CreateDirectory(dir);
		}
		return Directory.GetFiles(dir, "*.xml", SearchOption.TopDirectoryOnly);
	}

	public static NewAutoTuneExperiment LoadConfig(string name)
	{
		NewAutoTuneExperiment experiment = null;
		string path = Application.StartupPath + "\\NewAutotune\\" + name + ".xml";
		XMLFile.Open(path, ref experiment);
		return experiment;
	}

	public void StartTune()
	{
		Detector.finishReceive += Detector_finishReceive;
		isTuning = true;
		threadTune = new Thread(ThreadTune);
		threadTune.Start();
	}

	private void Detector_errorReceive(object sender, ErrorMsgEventArgs e)
	{
		LensManager.x2lens.Etp = 0.0;
		LensManager.Instance.SetEtp();
		StopTune();
		MessageBox.Show("质量数为" + e.X + "元素浓度过高，请检查，已停止测试！", "警告-倍增器保护");
	}

	public void StopTune()
	{
		isTuning = false;
		Detector.finishReceive -= Detector_finishReceive;
		if (threadTune != null && threadTune.IsAlive)
		{
			threadTune.Abort();
		}
		Detector.Instance.Stop();
		Detector.Instance.ExperimentState = -1;
	}

	private void Detector_finishReceive(object sender, EventArgs e)
	{
		scanEvent.Set();
	}

	private void ThreadTune()
	{
		TuneMsg($"Starting {Stages[0].StageName}");
		InitScanDatas(Stages[0]);
		for (int iterationIdx4 = 0; iterationIdx4 < Stages[0].Iterations; iterationIdx4++)
		{
			StageIndex = 1.0;
			TuneMsg("1");
			TuneMsg($"Iteration {iterationIdx4}");
			TuneStage1();
		}
		TuneMsg($"Starting {Stages[1].StageName}");
		InitScanDatas(Stages[1]);
		for (int iterationIdx3 = 0; iterationIdx3 < Stages[1].Iterations; iterationIdx3++)
		{
			StageIndex = 2.0;
			TuneMsg("2");
			TuneMsg($"Iteration {iterationIdx3}");
			TuneStage2();
		}
		TuneMsg($"Starting {Stages[2].StageName}");
		InitScanDatas(Stages[2]);
		for (int iterationIdx2 = 0; iterationIdx2 < Stages[2].Iterations; iterationIdx2++)
		{
			TuneMsg($"Iteration {iterationIdx2}");
			TuneStage3();
		}
		TuneMsg($"Starting {Stages[3].StageName}");
		InitScanDatas(Stages[3]);
		for (int iterationIdx = 0; iterationIdx < Stages[3].Iterations; iterationIdx++)
		{
			TuneMsg($"Iteration {iterationIdx}");
			TuneStage4();
		}
		TuneFinish(error: false);
	}

	private void TuneStage1()
	{
		TuneStage stage = Stages[0];
		TuneMsg($"Starting {stage.StageName}");
		InitScanDatas(stage);
		DeviceCondition deviceY = stage.Devices.Find((DeviceCondition r) => r.Id == 1);
		DeviceCondition deviceX = stage.Devices.Find((DeviceCondition r) => r.Id == 0);
		List<StageResult1> resultlist = new List<StageResult1>();
		for (double YY = deviceY.MinValue; YY < deviceY.MaxValue; YY += deviceY.Step)
		{
			PlatManager.plat.Z = (float)YY;
			Plasma.Instance.SetZAxis();
			TuneMsg($"Tuning {deviceY.Device},{deviceX.Device}");
			TuneMsg($"Tuning 垂直位置：{YY}");
			double maxmin = -1.0;
			if (!MaxMinTune1(stage, deviceX, out maxmin))
			{
				TuneMsg("Scan error.");
				TuneFinish(error: true);
				return;
			}
			resultlist.Add(new StageResult1
			{
				Y = YY,
				X = deviceX.BestValue,
				result = maxmin
			});
		}
		StageResult1 max = resultlist.OrderByDescending((StageResult1 a) => a.result).FirstOrDefault();
		PlatManager.plat.X = (float)max.X;
		deviceX.BestValue = max.X;
		Plasma.Instance.SetXAxis();
		PlatManager.plat.Z = (float)max.Y;
		Plasma.Instance.SetZAxis();
		deviceY.BestValue = max.Y;
		using IEnumerator<ParamConditon> enumerator = stage.Params.Where((ParamConditon p) => p.Type != 0).GetEnumerator();
		if (enumerator.MoveNext())
		{
			ParamConditon para = enumerator.Current;
			para.BestResult = max.result;
		}
	}

	private void TuneStage2()
	{
		TuneStage stage = Stages[1];
		TuneMsg($"Starting {stage.StageName}");
		InitScanDatas(stage);
		DeviceCondition devicePower = stage.Devices.Find((DeviceCondition r) => r.Id == 17);
		DeviceCondition deviceGas = stage.Devices.Find((DeviceCondition r) => r.Id == 13);
		List<StageResult2> resultlist = new List<StageResult2>();
		for (double P = devicePower.MinValue; P < devicePower.MaxValue; P += devicePower.Step)
		{
			PlasmaManager.plasma.Power = P;
			Plasma.Instance.SetPower();
			TuneMsg($"Tuning {devicePower.Device},{deviceGas.Device}");
			TuneMsg($"Tuning P:{P}");
			double maxmin = -1.0;
			if (!MaxMinTune1(stage, deviceGas, out maxmin))
			{
				TuneMsg("Scan error.");
				TuneFinish(error: true);
				return;
			}
			resultlist.Add(new StageResult2
			{
				Power = P,
				CarrierGas = deviceGas.BestValue,
				Result = maxmin
			});
		}
		StageResult2 max = resultlist.OrderByDescending((StageResult2 a) => a.Result).FirstOrDefault();
		PlasmaManager.plasma.Nebuliser = max.CarrierGas;
		Plasma.Instance.OpenNebulizerGas();
		devicePower.BestValue = max.Power;
		deviceGas.BestValue = max.CarrierGas;
		PlasmaManager.plasma.Power = max.Power;
		Plasma.Instance.SetPower();
		using IEnumerator<ParamConditon> enumerator = stage.Params.Where((ParamConditon p) => p.Type != 0).GetEnumerator();
		if (enumerator.MoveNext())
		{
			ParamConditon para = enumerator.Current;
			para.BestResult = max.Result;
		}
	}

	private void TuneStage3()
	{
		TuneStage stage = Stages[2];
		TuneMsg($"Starting {stage.StageName}");
		StageIndex = 3.0;
		TuneMsg("3.0");
		InitScanDatas(stage);
		DeviceCondition deviceExt = stage.Devices.Find((DeviceCondition r) => r.Id == 3);
		DeviceCondition deviceL1 = stage.Devices.Find((DeviceCondition r) => r.Id == 4);
		DeviceCondition deviceL2 = stage.Devices.Find((DeviceCondition r) => r.Id == 5);
		DeviceCondition deviceL3 = stage.Devices.Find((DeviceCondition r) => r.Id == 11);
		List<StageResult3> resultlist = new List<StageResult3>();
		for (double Extt = deviceExt.MinValue; Extt < deviceExt.MaxValue; Extt += deviceExt.Step)
		{
			LensManager.x2lens.Ex = Extt;
			LensManager.Instance.X2_SetEx();
			TuneMsg($"Tuning {deviceExt.Device},{deviceL1.Device},{deviceL2.Device}");
			TuneMsg($"Tuning Ext:{Extt}");
			double LL1 = deviceL1.MinValue;
			List<StageResult3> inResultlist = new List<StageResult3>();
			for (; LL1 < deviceL1.MaxValue; LL1 += deviceL1.Step)
			{
				TuneMsg($"Tuning Ext:{Extt},L1:{LL1}");
				LensManager.x2lens.L1 = LL1;
				LensManager.Instance.X2_SetL1();
				double maxmin = -1.0;
				if (!MaxMinTune1(stage, deviceL2, out maxmin))
				{
					TuneMsg("Scan error.");
					TuneFinish(error: true);
					return;
				}
				inResultlist.Add(new StageResult3
				{
					Ext = Extt,
					L1 = LL1,
					L2 = deviceL2.BestValue,
					result = maxmin
				});
			}
			StageResult3 max = inResultlist.OrderByDescending((StageResult3 a) => a.result).FirstOrDefault();
			resultlist.Add(max);
		}
		StageResult3 max2 = resultlist.OrderByDescending((StageResult3 a) => a.result).FirstOrDefault();
		LensManager.x2lens.Ex = max2.Ext;
		LensManager.Instance.X2_SetEx();
		LensManager.x2lens.L1 = max2.L1;
		LensManager.Instance.X2_SetL1();
		LensManager.x2lens.L2 = max2.L2;
		LensManager.Instance.X2_SetL2();
		StageIndex = 3.1;
		TuneMsg("3.1");
		TuneMsg($"Tuning {deviceL3.Device}");
		double maxmin2 = -1.0;
		if (!MaxMinTune1(stage, deviceL3, out maxmin2))
		{
			TuneMsg("Scan error.");
			TuneFinish(error: true);
			return;
		}
		LensManager.x2lens.L3 = deviceL3.BestValue;
		LensManager.Instance.X2_SetL3();
		deviceExt.BestValue = max2.Ext;
		deviceL1.BestValue = max2.L1;
		deviceL2.BestValue = max2.L2;
		deviceL3.BestValue = max2.L3;
		using IEnumerator<ParamConditon> enumerator = stage.Params.Where((ParamConditon p) => p.Type != 0).GetEnumerator();
		if (enumerator.MoveNext())
		{
			ParamConditon para = enumerator.Current;
			para.BestResult = maxmin2;
		}
	}

	private void TuneStage4()
	{
		TuneStage stage = Stages[3];
		TuneMsg($"Starting {stage.StageName}");
		StageIndex = 4.0;
		TuneMsg("4.0");
		InitScanDatas(stage);
		DeviceCondition deviceFocus = stage.Devices.Find((DeviceCondition r) => r.Id == 6);
		DeviceCondition deviceD1 = stage.Devices.Find((DeviceCondition r) => r.Id == 7);
		DeviceCondition deviceD2 = stage.Devices.Find((DeviceCondition r) => r.Id == 8);
		DeviceCondition deviceQE = stage.Devices.Find((DeviceCondition r) => r.Id == 9);
		DeviceCondition deviceDA = stage.Devices.Find((DeviceCondition r) => r.Id == 12);
		List<StageResult4> resultlist = new List<StageResult4>();
		double F = deviceFocus.MinValue;
		for (double StepF = deviceFocus.Step; F < deviceFocus.MaxValue; F += StepF)
		{
			LensManager.x2lens.F = F;
			LensManager.Instance.X2_SetF();
			TuneMsg($"Tuning {deviceFocus.Device},{deviceD1.Device},{deviceD2.Device}");
			double DD1 = deviceD1.MinValue;
			double StepD1 = deviceD1.Step;
			List<StageResult4> inResultlist = new List<StageResult4>();
			for (; DD1 < deviceD1.MaxValue; DD1 += StepD1)
			{
				TuneMsg($"Tuning F:{F:0.00},D1:{DD1:0.00}");
				LensManager.x2lens.D1 = DD1;
				LensManager.Instance.X2_SetD1();
				double maxmin = -1.0;
				if (!MaxMinTune1(stage, deviceD2, out maxmin))
				{
					TuneMsg("Scan error.");
					TuneFinish(error: true);
					return;
				}
				inResultlist.Add(new StageResult4
				{
					Focus = F,
					D1 = DD1,
					D2 = deviceD2.BestValue,
					Result = maxmin
				});
			}
			StageResult4 max = inResultlist.OrderByDescending((StageResult4 a) => a.Result).FirstOrDefault();
			resultlist.Add(max);
		}
		StageResult4 max2 = resultlist.OrderByDescending((StageResult4 a) => a.Result).FirstOrDefault();
		LensManager.x2lens.F = max2.Focus;
		LensManager.Instance.X2_SetF();
		LensManager.x2lens.D1 = max2.D1;
		LensManager.Instance.X2_SetD1();
		LensManager.x2lens.D2 = max2.D2;
		LensManager.Instance.X2_SetD2();
		deviceFocus.BestValue = max2.Focus;
		deviceD1.BestValue = max2.D1;
		deviceD2.BestValue = max2.D2;
		TuneMsg($"Tuning DA:{deviceDA.Device}");
		StageIndex = 4.1;
		TuneMsg("4.1");
		double maxmin2 = -1.0;
		if (!MaxMinTune1(stage, deviceDA, out maxmin2))
		{
			TuneMsg("Scan error.");
			TuneFinish(error: true);
			return;
		}
		LensManager.x2lens.DA = deviceDA.BestValue;
		LensManager.Instance.X2_SetDA();
		TuneMsg($"Tuning QE:{deviceQE.Device}");
		StageIndex = 4.2;
		TuneMsg("4.2");
		double maxminQE = -1.0;
		if (!MaxMinTune1(stage, deviceQE, out maxminQE))
		{
			TuneMsg("Scan error.");
			TuneFinish(error: true);
			return;
		}
		LensManager.x2lens.QE = deviceQE.BestValue;
		LensManager.Instance.X2_SetQE();
		using IEnumerator<ParamConditon> enumerator = stage.Params.Where((ParamConditon p) => p.Type != 0).GetEnumerator();
		if (enumerator.MoveNext())
		{
			ParamConditon para = enumerator.Current;
			para.BestResult = maxmin2;
		}
	}

	private void TuneFinish(bool error)
	{
		TuneSucceed = !error;
		if (this.EventTuneFinish != null)
		{
			this.EventTuneFinish(null, null);
		}
	}

	private void TuneMsg(string msg)
	{
		if (this.EventTuneMsg != null)
		{
			this.EventTuneMsg(null, new MsgEventArgs(msg));
		}
	}

	private bool MaxMinTune1(TuneStage stage, DeviceCondition device, out double BestResult)
	{
		BestResult = -1.0;
		ParamConditon tunePara = stage.Params.Where((ParamConditon para) => para.Type == 1 || para.Type == 2).First();
		List<double> devValues = new List<double>();
		List<double> paraValues = new List<double>();
		double step = device.Step;
		for (double tmp = device.MaxValue; tmp > device.MinValue; tmp -= step)
		{
			devValues.Add(tmp);
		}
		for (int i = 0; i < devValues.Count; i++)
		{
			if (!TuneDevice(stage, device, devValues[i]))
			{
				return false;
			}
			paraValues.Add(tunePara.Result);
			double stageIndex = StageIndex;
			double num = stageIndex;
			if (num <= 3.0)
			{
				if (num != 1.0)
				{
					if (num != 2.0)
					{
						if (num == 3.0)
						{
							TunePoinSend(LensManager.x2lens.ReadEx, LensManager.x2lens.ReadL1, LensManager.x2lens.ReadL2, tunePara.Result);
						}
					}
					else
					{
						TunePoinSend(PlasmaManager.plasma.ReadForward, PlasmaManager.plasma.ReadNebuliser, 0.0, tunePara.Result);
					}
				}
				else
				{
					TunePoinSend(PlatManager.plat.ReadZ / 100.0, PlatManager.plat.ReadX / 100.0, 0.0, tunePara.Result);
				}
			}
			else if (num <= 4.0)
			{
				if (num != 3.1)
				{
					if (num == 4.0)
					{
						TunePoinSend(LensManager.x2lens.ReadF, LensManager.x2lens.ReadD1, LensManager.x2lens.ReadD2, tunePara.Result);
					}
				}
				else
				{
					TunePoinSend(LensManager.x2lens.ReadL3, 0.0, 0.0, tunePara.Result);
				}
			}
			else if (num != 4.1)
			{
				if (num == 4.2)
				{
					TunePoinSend(LensManager.x2lens.ReadQE, 0.0, 0.0, tunePara.Result);
				}
			}
			else
			{
				TunePoinSend(LensManager.x2lens.ReadDA, 0.0, 0.0, tunePara.Result);
			}
		}
		int maxminIdx = MaxMinIndex(paraValues.ToArray(), max: true);
		BestResult = paraValues[maxminIdx];
		device.BestValue = devValues[maxminIdx];
		return true;
	}

	public void TunePoinSend(double x, double y, double z, double intensity)
	{
		if (this.EventPointMsg != null)
		{
			this.EventPointMsg(null, new PointEventMsg(x, y, z, intensity));
		}
	}

	private bool MaxMinTune(TuneStage stage, DeviceCondition device, out double[] maxmin)
	{
		maxmin = new double[3];
		ParamConditon tunePara = stage.Params.Where((ParamConditon para) => para.Type == 1 || para.Type == 2).First();
		List<double> devValues = new List<double>();
		List<double> paraValues = new List<double>();
		double step = (device.MaxValue - device.MinValue) / 20.0;
		for (double tmp = device.MaxValue; tmp > device.MinValue; tmp -= step)
		{
			devValues.Add(tmp);
		}
		for (int i = 0; i < devValues.Count; i++)
		{
			if (!TuneDevice(stage, device, devValues[i]))
			{
				return false;
			}
			paraValues.Add(tunePara.Result);
		}
		int maxminIdx = MaxMinIndex(paraValues.ToArray(), tunePara.Type == 1);
		double max;
		double center;
		double min;
		if (maxminIdx == 0)
		{
			max = devValues[0];
			center = devValues[1];
			min = devValues[2];
		}
		else if (maxminIdx == devValues.Count - 1)
		{
			max = devValues[maxminIdx - 2];
			center = devValues[maxminIdx - 1];
			min = devValues[maxminIdx];
		}
		else
		{
			max = devValues[maxminIdx - 1];
			center = devValues[maxminIdx];
			min = devValues[maxminIdx + 1];
		}
		maxmin[0] = max;
		maxmin[1] = center;
		maxmin[2] = min;
		return true;
	}

	private bool HalfTune(TuneStage stage, DeviceCondition device, double max, double center, double min)
	{
		ParamConditon tunePara = stage.Params.Where((ParamConditon para) => para.Type == 1 || para.Type == 2).First();
		double[] devValues = new double[3] { max, center, min };
		double[] paraValues = new double[3];
		do
		{
			if (!TuneDevice(stage, device, devValues[0]))
			{
				return false;
			}
			paraValues[0] = tunePara.Result;
			if (!TuneDevice(stage, device, devValues[2]))
			{
				return false;
			}
			paraValues[2] = tunePara.Result;
			if (!TuneDevice(stage, device, devValues[1]))
			{
				return false;
			}
			paraValues[1] = tunePara.Result;
			if (tunePara.Type == 1)
			{
				switch (MaxMinIndex(paraValues, max: true))
				{
				case 0:
					max = devValues[0];
					center = (devValues[0] + devValues[1]) / 2.0;
					min = devValues[1];
					break;
				case 1:
					max = (devValues[0] + devValues[1]) / 2.0;
					center = devValues[1];
					min = (devValues[1] + devValues[2]) / 2.0;
					break;
				default:
					max = devValues[1];
					center = (devValues[1] + devValues[2]) / 2.0;
					min = devValues[2];
					break;
				}
				devValues[0] = max;
				devValues[1] = center;
				devValues[2] = min;
			}
			else
			{
				switch (MaxMinIndex(paraValues, max: false))
				{
				case 0:
					max = devValues[0];
					center = (devValues[0] + devValues[1]) / 2.0;
					min = devValues[1];
					break;
				case 1:
					max = (devValues[0] + devValues[1]) / 2.0;
					center = devValues[1];
					min = (devValues[1] + devValues[2]) / 2.0;
					break;
				default:
					max = devValues[1];
					center = (devValues[1] + devValues[2]) / 2.0;
					min = devValues[2];
					break;
				}
				devValues[0] = max;
				devValues[1] = center;
				devValues[2] = min;
			}
			device.BestValue = devValues[0];
		}
		while (devValues[0] - devValues[1] >= device.FinishErr && devValues[1] - devValues[2] >= device.FinishErr);
		return true;
	}

	private int MaxMinIndex(double[] datas, bool max)
	{
		int idx = 0;
		for (int i = 1; i < datas.Length; i++)
		{
			idx = (max ? ((datas[idx] > datas[i]) ? idx : i) : ((datas[idx] < datas[i]) ? idx : i));
		}
		return idx;
	}

	private bool TuneDevice(TuneStage stage, DeviceCondition device, double value)
	{
		TuneMsg($"{device.Device} set to {value:0.00}");
		SetDeviceValue(device.Id, value);
		int count = 0;
		while (!CheckPosition(value, device))
		{
			count++;
			Thread.Sleep(1000);
			if (count >= 15)
			{
				return false;
			}
		}
		Thread.Sleep(2000);
		scanEvent.Reset();
		Detector.msDetectorPara.InitIntensity();
		List<int> res = new List<int>();
		for (int j = 0; j < massX.Count; j++)
		{
			res.Add(1);
		}
		for (int i = 0; i < stage.Sweeps; i++)
		{
			if (i == 0)
			{
				Detector.Instance.SetMassAxis(massX.ToArray());
			}
			Detector.Instance.SendHopDatas(massX.ToArray(), res.ToArray(), true, false);
			if (!scanEvent.WaitOne(30000))
			{
				return false;
			}
		}
		CalculateResults(stage, device, value);
		return true;
	}

	private void CalculateResults(TuneStage stage, DeviceCondition device, double value)
	{
		double[] mass = Detector.msDetectorPara.MassAxis.ToArray();
		double[] intensity = Detector.msDetectorPara.IntesnsityPulse.ToArray();
		foreach (ParamConditon para2 in stage.Params.Where((ParamConditon p) => p.Type != 0))
		{
			if (para2.Isotope != null)
			{
				para2.Result = intensity.Where((double data, int index) => mass[index] >= para2.Isotope.Mass - 0.1 && mass[index] <= para2.Isotope.Mass + 0.1).Average();
				para2.Result /= stage.Sweeps;
			}
			else if (para2.Ratio != null)
			{
				double numerator = intensity.Where((double data, int index) => mass[index] >= para2.Ratio.Numerator.Mass - 0.1 && mass[index] <= para2.Ratio.Numerator.Mass + 0.1).Average();
				double denominator = intensity.Where((double data, int index) => mass[index] >= para2.Ratio.Denominator.Mass - 0.1 && mass[index] <= para2.Ratio.Denominator.Mass + 0.1).Average();
				para2.Result = numerator / denominator;
			}
			if ((para2.Type == 1 || para2.Type == 3) && para2.Result < para2.Limit)
			{
				para2.Pass = false;
			}
			else if ((para2.Type == 2 || para2.Type == 4) && para2.Result > para2.Limit)
			{
				para2.Pass = false;
			}
			else
			{
				para2.Pass = true;
			}
			TuneMsg($"Result for {para2.Symbol} = {para2.Result:0.000} ");
		}
		if (stage.Params.Exists((ParamConditon para) => para.Type != 0 && !para.Pass))
		{
			return;
		}
		foreach (ParamConditon param in stage.Params)
		{
			param.BestResult = param.Result;
		}
	}

	private void InitScanDatas(TuneStage stage)
	{
		massX.Clear();
		List<TuneIsotope> isotopes = new List<TuneIsotope>();
		foreach (ParamConditon para in stage.Params.Where((ParamConditon p) => p.Type != 0))
		{
			if (para.Isotope != null)
			{
				isotopes.Add(para.Isotope);
			}
			else if (para.Ratio != null)
			{
				TuneIsotope numerator = para.Ratio.Numerator;
				TuneIsotope denominator = para.Ratio.Denominator;
				if (!isotopes.Contains(numerator))
				{
					isotopes.Add(numerator);
				}
				if (!isotopes.Contains(denominator))
				{
					isotopes.Add(denominator);
				}
			}
		}
		foreach (TuneIsotope isotope in isotopes)
		{
			massX.Add(isotope.Mass);
		}
	}

	private void SetDeviceValue(int id, double value)
	{
		switch (id)
		{
		case 0:
			PlatManager.plat.X = (float)value;
			Plasma.Instance.SetXAxis();
			break;
		case 1:
			PlatManager.plat.Z = (float)value;
			Plasma.Instance.SetZAxis();
			break;
		case 2:
			PlatManager.plat.Y = (float)value;
			Plasma.Instance.SetYAxis();
			break;
		case 3:
			LensManager.x2lens.Ex = value;
			LensManager.Instance.X2_SetEx();
			break;
		case 4:
			LensManager.x2lens.L1 = value;
			LensManager.Instance.X2_SetL1();
			break;
		case 5:
			LensManager.x2lens.L2 = value;
			LensManager.Instance.X2_SetL2();
			break;
		case 6:
			LensManager.x2lens.F = value;
			LensManager.Instance.X2_SetF();
			break;
		case 7:
			LensManager.x2lens.D1 = value;
			LensManager.Instance.X2_SetD1();
			break;
		case 8:
			LensManager.x2lens.D2 = value;
			LensManager.Instance.X2_SetD2();
			break;
		case 9:
			LensManager.x2lens.QE = value;
			LensManager.Instance.X2_SetQE();
			break;
		case 10:
			LensManager.x2lens.Pole = value;
			LensManager.Instance.X2_SetPole();
			break;
		case 11:
			LensManager.x2lens.L3 = value;
			LensManager.Instance.X2_SetL3();
			break;
		case 12:
			LensManager.x2lens.DA = value;
			LensManager.Instance.X2_SetDA();
			break;
		case 13:
			PlasmaManager.plasma.Nebuliser = value;
			Plasma.Instance.OpenNebulizerGas();
			break;
		case 14:
			PlasmaManager.plasma.Cool = value;
			Plasma.Instance.OpenCoolGas();
			break;
		case 15:
			PlasmaManager.plasma.Auxiliary = value;
			Plasma.Instance.OpenAuxiliaryGas();
			break;
		}
	}
}
